package org.vacoor.nothing.misc.express.usps.api;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vacoor.nothing.common.util.HttpUtils;
import org.vacoor.nothing.common.util.IOUtils;
import org.vacoor.nothing.common.xml.JAXB2;
import org.vacoor.nothing.misc.express.usps.api.support.StdUspsResultBuilder;

import java.io.InputStream;
import java.util.Map;

/**
 * USPS 工具类
 *
 * @author vacoor
 */
public class Usps {
    // 有些接口必须使用 https
    // private static final String DEFAULT_SERVICE_URL = "http://production.shippingapis.com/ShippingAPI.dll";
    private static final String DEFAULT_SERVICE_URL = "https://secure.shippingapis.com/ShippingAPI.dll";
    private static final String API_PARAM = "API";
    private static final String XML_PARAM = "XML";
    private static final Logger LOG = LoggerFactory.getLogger(Usps.class);

    private String serviceUrl;
    private String userId;
    private String password;
    private int timeout = 10 * 1000;

    public static Usps create(String userId, String password) {
        return create(DEFAULT_SERVICE_URL, userId, password);
    }

    public static Usps create(String serviceUrl, String userId, String password) {
        return new Usps(serviceUrl, userId, password);
    }

    /**
     * Post USPS 请求到服务器
     *
     * @param req
     * @param targetType
     * @param <T>
     * @return
     */
    public <T> UspsResult<T> postRequest(UspsRequest req, Class<T> targetType) {
        return postRequest(req, StdUspsResultBuilder.create(targetType));
    }

    /**
     * Post USPS 请求到服务器, 并使用给定 ResultBuilder 来处理结果
     *
     * @param req
     * @param builder
     * @param <T>
     * @return
     */
    public <T> UspsResult<T> postRequest(UspsRequest req, UspsResult.Builder<T> builder) {
        Class<? extends UspsRequest> clazz = req.getClass();
        String api;
        try {
            api = (String) clazz.getField("API").get(null);
        } catch (Exception e) {
            throw new UspsException("can't find public static final field 'API' from " + clazz);
        }
        return callApi(api, req, builder);
    }

    /**
     * 调用给定 USPS API 接口, 并使用给定接口处理结果
     *
     * @param api     接口名称, eg: Verify
     * @param req     USPS 请求
     * @param builder 结果 builder
     * @param <T>
     * @return
     */
    public <T> UspsResult<T> callApi(String api, UspsRequest req, UspsResult.Builder<T> builder) {
        String xml = doCallUspsApi(serviceUrl, api, req);
        return builder.build(req, xml);
    }

    /**
     * 调用 USPS API
     *
     * @param api
     * @param req
     * @return
     */
    private String doCallUspsApi(String serviceUrl, String api, UspsRequest req) {
        req.setUserId(userId);
        req.setPassword(password);

        return doCallUspsApi(serviceUrl, api, JAXB2.marshal(req));
    }

    public String doCallUspsApi(String serviceUrl, String api, String xml) {
        Map<String, String> data = ImmutableMap.of(API_PARAM, api, XML_PARAM, xml);

        if (LOG.isDebugEnabled()) {
            LOG.debug("invoke usps api, url: {}, xml: {}", serviceUrl, data);
        }

        try {
            InputStream is = HttpUtils.post(serviceUrl, data, timeout);
            if (null != is) {
                String ret = IOUtils.readText(is, Charsets.UTF_8, true);

                if (LOG.isDebugEnabled()) {
                    LOG.debug("USPS Response: {}", ret);
                }

                return ret;
            }

            throw new UspsException("invoke usps api fail, not response");
        } catch (Exception e) {
            throw new UspsException("invoke usps api fail", e);
        }
    }

    private Usps(String serviceUrl, String userId, String password) {
        this.serviceUrl = serviceUrl;
        this.userId = userId;
        this.password = password;
    }
}
